package com.zhang.authoritycenter.common.config;

import com.zhang.authoritycenter.common.properties.BaseProperties;
import com.zhang.authoritycenter.security.JwtAccessDeniedHandler;
import com.zhang.authoritycenter.security.JwtAuthenticationEntryPoint;
import com.zhang.authoritycenter.security.jwt.JWTConfigurer;
import com.zhang.authoritycenter.security.jwt.TokenProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.CollectionUtils;
import org.springframework.web.filter.CorsFilter;

import java.util.List;

/**
 * description
 *
 * @author zhangFanJun
 * @date 2023-12-04 16:28
 **/
@RequiredArgsConstructor
@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final TokenProvider tokenProvider;
//    private final CorsFilter corsFilter;
    private final JwtAuthenticationEntryPoint authenticationErrorHandler;
    private final JwtAccessDeniedHandler jwtAccessDeniedHandler;
    private final BaseProperties baseProperties;

    /**
     * 相对于MD5而言，安全性更高，但是计算复杂，性能较差
     * MD5用于密码保存，适合每次登录，BCrypt用于支付安全
     * */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 因为不使用原生的登录，所以这里不需要
     */
//    @Bean
//    @Override
//    public AuthenticationManager authenticationManagerBean() throws Exception {
//        return super.authenticationManagerBean();
//    }


//    @Override
//    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
////        super.configure(auth);
//        // UserDetailsService类，代替了重写userDetailsService方式生成bean
//        auth.userDetailsService(userService)
//                // 加密策略
//                .passwordEncoder(passwordEncoder);
//    }


    // Configure paths and requests that should be ignored by Spring Security ================================

    @Override
    public void configure(WebSecurity web) {
        web.ignoring()
                .antMatchers(HttpMethod.OPTIONS, "/**")

                // allow anonymous resource requests
                .antMatchers(
                        "/",
                        "/*.html",
                        "/favicon.ico",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js",
                        "/h2-console/**"
                );
    }

    // Configure security settings ===========================================================================

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                // 启用CSRF，使用token验证
                .csrf()
                .disable()
                // 在用户密码校验之前，使用CorsFilter过滤器
//                .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)

                // 异常处理
                .exceptionHandling()
                .accessDeniedHandler(jwtAccessDeniedHandler)
                // 权限校验，比如ROLE_USER, ROLE_ADMIN
                .authenticationEntryPoint(authenticationErrorHandler)
                // 访问拒绝处理器

//                .and()
//                .formLogin()
//                .loginPage("/login")
//                .permitAll()
//                .and()
//                .logout()
//                .permitAll()
                // enable h2-console
                .and()
                // 将安全标志添加响应头部
                .headers()
                // 支持自定义
                .frameOptions()
                // 允许相同来源的请求
                .sameOrigin()

                // create no session
                .and()
                // session会话管理，用于下面关闭session
                .sessionManagement()
                // STATELESS:Spring Security永远不会创建HttpSession ，也永远不会使用它 获取SecurityContext
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)

                .and()
                .authorizeRequests()
                // 统一路径前缀不在考虑范围
//                .antMatchers("/authorityCenter/login").permitAll()
                .antMatchers("/register").permitAll()
                .antMatchers("/login").permitAll()
                .antMatchers("/auth/getUser").permitAll()
                .antMatchers("/auth/checkToken").permitAll()
                .antMatchers("/auth/openToken").permitAll()
                .antMatchers("/secret/delete/**").permitAll()
                .antMatchers("/info").hasAnyAuthority("ROLE_USER", "ROLE_ADMIN")
                .anyRequest().authenticated()

                .and()
                // 添加jwt过滤器，处理token（这里并没有校验用户）
                .apply(securityConfigurerAdapter());
        List<String> ignoreUriList = baseProperties.getIgnoreUriList();
        if(!CollectionUtils.isEmpty(ignoreUriList)){
            for (String uri : ignoreUriList) {
                httpSecurity.authorizeRequests().antMatchers(uri).permitAll();
            }
        }
    }

    private JWTConfigurer securityConfigurerAdapter() {
        return new JWTConfigurer(tokenProvider);
    }


}
